import {
  Injectable,
  Logger,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { ConfigService } from '@nestjs/config';
import { RoomsService } from '../rooms/rooms.service';
import { RedisService } from '../redis/redis.service';
import {
  JoinRoomMessageDto,
  LeaveRoomMessageDto,
  MediaStateMessageDto,
  ChatMessageDto,
  NetworkQualityMessageDto,
} from './dto/websocket-message.dto';
import { v4 as uuidv4 } from 'uuid';

interface ConnectionInfo {
  socketId: string;
  userId: string;
  roomId: string;
  connectedAt: Date;
  lastActivity: Date;
}

interface ChatMessage {
  id: string;
  content: string;
  userId: string;
  nickname?: string;
  timestamp: Date;
  type: 'text' | 'system';
}

@Injectable()
export class WebsocketService {
  private readonly logger = new Logger(WebsocketService.name);
  private server: Server;
  private connections = new Map<string, ConnectionInfo>();
  private userSockets = new Map<string, string>(); // userId -> socketId
  private roomParticipants = new Map<string, Set<string>>(); // roomId -> Set<userId>

  constructor(
    private readonly roomsService: RoomsService,
    private readonly redisService: RedisService,
    private readonly configService: ConfigService,
  ) {
    // 定期清理过期连接
    setInterval(() => {
      this.cleanupExpiredConnections();
    }, 60000); // 每分钟清理一次
  }

  setServer(server: Server) {
    this.server = server;
  }

  // 验证连接
  async validateConnection(
    client: Socket,
    userId: string,
    roomId: string,
  ): Promise<void> {
    try {
      // 验证房间是否存在
      const room = await this.roomsService.getRoomById(roomId);

      if (!room) {
        throw new NotFoundException('房间不存在');
      }

      if (room.isExpired) {
        throw new BadRequestException('房间已过期');
      }

      if (room.status === 'ended') {
        throw new BadRequestException('房间已结束');
      }

      // 检查房间是否已满（如果用户不在房间中）
      const isUserInRoom = await this.redisService.sIsMember(
        `room:${roomId}:participants`,
        userId,
      );

      if (!isUserInRoom && room.isFull) {
        throw new BadRequestException('房间已满');
      }

      this.logger.log(`连接验证通过: 用户 ${userId} 房间 ${roomId}`);
    } catch (error) {
      this.logger.error(`连接验证失败: ${error.message}`);
      throw error;
    }
  }

  // 存储连接信息
  async storeConnection(
    socketId: string,
    userId: string,
    roomId: string,
  ): Promise<void> {
    const connectionInfo: ConnectionInfo = {
      socketId,
      userId,
      roomId,
      connectedAt: new Date(),
      lastActivity: new Date(),
    };

    this.connections.set(socketId, connectionInfo);
    this.userSockets.set(userId, socketId);

    // 添加到房间参与者列表
    if (!this.roomParticipants.has(roomId)) {
      this.roomParticipants.set(roomId, new Set());
    }
    this.roomParticipants.get(roomId)!.add(userId);

    // 存储到Redis
    await this.redisService.setUserSession(userId, {
      socketId,
      roomId,
      connectedAt: connectionInfo.connectedAt,
      lastActivity: connectionInfo.lastActivity,
    });

    this.logger.log(`连接信息已存储: ${socketId} -> ${userId}`);
  }

  // 获取连接信息
  async getConnection(socketId: string): Promise<ConnectionInfo | null> {
    return this.connections.get(socketId) || null;
  }

  // 处理断开连接
  async handleDisconnection(socketId: string): Promise<void> {
    const connectionInfo = this.connections.get(socketId);

    if (connectionInfo) {
      const { userId, roomId } = connectionInfo;

      // 从内存中移除
      this.connections.delete(socketId);
      this.userSockets.delete(userId);

      // 从房间参与者列表中移除
      const roomParticipants = this.roomParticipants.get(roomId);
      if (roomParticipants) {
        roomParticipants.delete(userId);
        if (roomParticipants.size === 0) {
          this.roomParticipants.delete(roomId);
        }
      }

      // 从Redis中移除
      await this.redisService.deleteUserSession(userId);
      await this.redisService.removeParticipantFromRoom(roomId, userId);

      // 更新数据库中的参与者状态
      try {
        await this.roomsService.leaveRoom(roomId, userId);
      } catch (error) {
        this.logger.error(`更新数据库参与者状态失败: ${error.message}`);
      }

      this.logger.log(`连接已清理: ${socketId} -> ${userId}`);
    }
  }

  // 处理加入房间
  async handleJoinRoom(client: Socket, data: JoinRoomMessageDto) {
    try {
      // 调用房间服务加入房间
      const result = await this.roomsService.joinRoom(
        {
          roomId: data.roomId,
          password: data.password,
          nickname: data.nickname,
          avatar: data.avatar,
          mediaDevices: data.mediaDevices,
          metadata: data.metadata,
        },
        data.userId,
        client.id,
        this.extractClientIp(client),
        client.handshake.headers['user-agent'],
      );

      // 更新连接信息
      const connectionInfo = this.connections.get(client.id);
      if (connectionInfo) {
        connectionInfo.lastActivity = new Date();
      }

      // 获取房间内其他参与者
      const participants = await this.getRoomParticipants(data.roomId);

      return {
        room: result.room,
        participant: result.participant,
        participants,
      };
    } catch (error) {
      this.logger.error(`处理加入房间失败: ${error.message}`);
      throw error;
    }
  }

  // 处理离开房间
  async handleLeaveRoom(
    client: Socket,
    data: LeaveRoomMessageDto,
  ): Promise<void> {
    try {
      await this.roomsService.leaveRoom(data.roomId, data.userId);

      // 更新连接信息
      const connectionInfo = this.connections.get(client.id);
      if (connectionInfo) {
        connectionInfo.lastActivity = new Date();
      }
    } catch (error) {
      this.logger.error(`处理离开房间失败: ${error.message}`);
      throw error;
    }
  }

  // 处理媒体状态变更
  async handleMediaStateChange(
    client: Socket,
    data: MediaStateMessageDto,
  ): Promise<void> {
    try {
      await this.roomsService.updateParticipant(data.roomId, data.userId, {
        audioEnabled: data.audioEnabled,
        videoEnabled: data.videoEnabled,
        screenShareEnabled: data.screenShareEnabled,
      });

      // 更新连接信息
      const connectionInfo = this.connections.get(client.id);
      if (connectionInfo) {
        connectionInfo.lastActivity = new Date();
      }
    } catch (error) {
      this.logger.error(`处理媒体状态变更失败: ${error.message}`);
      throw error;
    }
  }

  // 处理聊天消息
  async handleChatMessage(
    client: Socket,
    data: ChatMessageDto,
  ): Promise<ChatMessage> {
    try {
      const message: ChatMessage = {
        id: uuidv4(),
        content: data.content,
        userId: data.userId,
        nickname: data.nickname,
        timestamp: new Date(),
        type: data.type || 'text',
      };

      // 存储聊天消息到Redis（可选）
      const chatKey = `chat:${data.roomId}`;
      await this.redisService.lPush(chatKey, JSON.stringify(message));
      await this.redisService.expire(chatKey, 3600); // 1小时过期

      // 更新连接信息
      const connectionInfo = this.connections.get(client.id);
      if (connectionInfo) {
        connectionInfo.lastActivity = new Date();
      }

      return message;
    } catch (error) {
      this.logger.error(`处理聊天消息失败: ${error.message}`);
      throw error;
    }
  }

  // 处理网络质量更新
  async handleNetworkQuality(
    client: Socket,
    data: NetworkQualityMessageDto,
  ): Promise<void> {
    try {
      await this.roomsService.updateParticipant(data.roomId, data.userId, {
        // 这里需要扩展UpdateParticipantDto来支持网络质量
      });

      // 更新连接信息
      const connectionInfo = this.connections.get(client.id);
      if (connectionInfo) {
        connectionInfo.lastActivity = new Date();
      }
    } catch (error) {
      this.logger.error(`处理网络质量更新失败: ${error.message}`);
      throw error;
    }
  }

  // 获取用户的Socket ID
  async getUserSocketId(
    roomId: string,
    userId: string,
  ): Promise<string | null> {
    return this.userSockets.get(userId) || null;
  }

  // 获取房间参与者
  async getRoomParticipants(roomId: string): Promise<any[]> {
    try {
      const room = await this.roomsService.getRoomById(roomId, true);
      return room.participants || [];
    } catch (error) {
      this.logger.error(`获取房间参与者失败: ${error.message}`);
      return [];
    }
  }

  // 获取房间内在线用户数
  getRoomOnlineCount(roomId: string): number {
    const participants = this.roomParticipants.get(roomId);
    return participants ? participants.size : 0;
  }

  // 获取总在线用户数
  getTotalOnlineCount(): number {
    return this.connections.size;
  }

  // 获取房间列表
  getActiveRooms(): string[] {
    return Array.from(this.roomParticipants.keys());
  }

  // 广播系统消息到房间
  async broadcastSystemMessage(roomId: string, message: string): Promise<void> {
    if (this.server) {
      const systemMessage: ChatMessage = {
        id: uuidv4(),
        content: message,
        userId: 'system',
        timestamp: new Date(),
        type: 'system',
      };

      this.server.to(roomId).emit('chat-message', systemMessage);
    }
  }

  // 踢出用户
  async kickUser(
    roomId: string,
    userId: string,
    reason?: string,
  ): Promise<void> {
    const socketId = this.userSockets.get(userId);
    if (socketId && this.server) {
      this.server.to(socketId).emit('kicked', {
        roomId,
        reason: reason || '您已被移出房间',
        timestamp: new Date(),
      });

      // 断开连接
      const socket = this.server.sockets.sockets.get(socketId);
      if (socket) {
        socket.disconnect();
      }
    }
  }

  // 清理过期连接
  private cleanupExpiredConnections(): void {
    const now = new Date();
    const expireTime = 30 * 60 * 1000; // 30分钟

    for (const [socketId, connectionInfo] of this.connections.entries()) {
      if (now.getTime() - connectionInfo.lastActivity.getTime() > expireTime) {
        this.logger.log(`清理过期连接: ${socketId}`);
        this.handleDisconnection(socketId);
      }
    }
  }

  // 获取连接统计信息
  getConnectionStats() {
    const roomStats = new Map<string, number>();
    for (const [roomId, participants] of this.roomParticipants.entries()) {
      roomStats.set(roomId, participants.size);
    }

    return {
      totalConnections: this.connections.size,
      totalRooms: this.roomParticipants.size,
      roomStats: Object.fromEntries(roomStats),
      timestamp: new Date(),
    };
  }

  // 私有辅助方法
  private extractClientIp(client: Socket): string {
    return (
      (client.handshake.headers['x-forwarded-for'] as string) ||
      client.handshake.address ||
      '127.0.0.1'
    );
  }
}
